using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;

namespace Cavy.UdpClient.SystemUdpClient
{
    public class TransportNetCoreServerClient : Transport
    {
        private EchoUdpClient _udpClient;
        private Dictionary<string, Dictionary<int, EndPoint>> _endpoints =
            new Dictionary<string, Dictionary<int, EndPoint>>();
        public override int Bind(int port = 0)
        {
            return port;
        }

        public override void Connect(string address, int port)
        {
            _udpClient = new EchoUdpClient(IPAddress.Parse(address), port);
            _udpClient.OptionReceiveBufferSize = ReceiveBufferSize;
            _udpClient.OnConnectedEvent -= CallOnConnected;
            _udpClient.OnConnectedEvent += CallOnConnected;
            _udpClient.Connect();
        }

        public override bool EnableBroadcast { get; set; }

        public override int ReceiveBufferSize
        {
            get => _udpClient.OptionReceiveBufferSize;
            set => _udpClient.OptionReceiveBufferSize = value;
        }
        public override void Close()
        {
            _udpClient.Disconnect();
            IsReceiving = false;
        }

        public override void Dispose()
        {
            _udpClient.Dispose();
        }

        public override void ReceiveAsync()
        {
            if(IsReceiving) return;
            IsReceiving = true;
            _udpClient.OnReceivedEvent += (point, bytes, arg3, arg4) =>
            {
                CallOnReceived(new UdpResult()
                {
                    Buffer = bytes
                });
            };
            _udpClient.ReceiveAsync();
        }

        public override void Send(byte[] data, int dataLength, string host, int port)
        {
            if (!_endpoints.TryGetValue(host, out var address))
            {
                _endpoints[host] = address = new Dictionary<int, EndPoint>();
                
            }
            if (!address.TryGetValue(port, out var endPoint))
            {
                endPoint = new IPEndPoint(IPAddress.Parse(host), port);
                address[port] = endPoint;
            }
            _udpClient.Send(endPoint, data);
        }

        class EchoUdpClient : NetCoreServer.UdpClient
        {
            public bool Connected { get; set; }
            public bool Disconnected { get; set; }
            public bool Errors { get; set; }

            public EchoUdpClient(IPAddress address, int port) : base(address, port) {}
            public event System.Action<EndPoint, byte[], long, long> OnReceivedEvent;
            public event System.Action OnConnectedEvent; 
            public event System.Action OnDisconnectedEvent;
            public event System.Action OnErrorEvent;

            protected override void OnConnected() 
            { 
                Connected = true;
                OnConnectedEvent?.Invoke();
            }

            protected override void OnDisconnected()
            {
                Disconnected = true; 
                OnDisconnectedEvent?.Invoke();
            }

            protected override void OnReceived(EndPoint endpoint, byte[] buffer, long offset, long size)
            {
                OnReceivedEvent ?.Invoke(endpoint, buffer, offset, size); 
                ReceiveAsync();
            }

            protected override void OnError(SocketError error)
            {
                UdpDebug.LogError("Socket Error: " + error.ToString());
                Errors = true;
                OnErrorEvent?.Invoke();
            }
        }
    }
}